Análisis Exploratorio de Dataset de Airbnb (Madrid-España)

Este análisis explora un conjunto de datos de Airbnb, específicamente centrado en las listas de alojamientos disponibles en Madrid, España. Los datos han sido descargados desde el portal público OpenDataSoft, una plataforma que facilita el acceso a diversos datasets abiertos para análisis y aplicaciones de datos. Puedes acceder directamente al dataset utilizado a través del siguiente enlace: Airbnb Listings en OpenDataSoft.

Instalacion y Carga de Pre-requerimientos

Previo al análisis, vamos a instalar y cargar los paquetes necesarios:

# Silas librerias no están ya instaladas , las instala. 
if (!require("tidyverse")) install.packages("tidyverse")
if (!require("corrplot")) install.packages("corrplot")
if (!require("ggmap")) install.packages("ggmap")
if (!require("leaflet")) install.packages("leaflet")
if (!require("leaflet.extras")) install.packages("leaflet.extras")
if (!require("GGally")) install.packages("GGally")
if (!require(utils)) {library(utils)
}

# Cargar las librerías
library(tidyverse)
library(corrplot)
library(ggmap)
library(leaflet)
library(leaflet.extras)
library(GGally)

Descripción de Librerías de R que hemos utilizado:

Tidyverse: La librería tidyverse carga en R un conjunto de paquetes que comparten una filosofía común de manipulación y visualización de datos, incluyendo dplyr, ggplot2 y otros paquetes que facilitan el flujo de trabajo de análisis de datos en R.

Dplyr: Proporciona un conjunto de funciones para realizar operaciones de manipulación de datos como filtrado, selección, agregación y unión de datos.

Ggplot2: Permite crear una amplia variedad de gráficos de alta calidad de forma sencilla y flexible.

Corrplot: Se utiliza para visualizar matrices de correlación de datos de una manera clara y concisa, lo que facilita la identificación de patrones de correlación entre variables.

Ggmap: Esta librería se utiliza para visualizar datos espaciales en mapas utilizando ggplot2. Permite superponer datos sobre mapas de Google Maps, OpenStreetMap y otros proveedores de mapas.

Leaflet: Es una librería para la creación de mapas interactivos en R. Permite crear mapas interactivos con características como marcadores, polígonos, superposiciones y herramientas de zoom.

Leaflet.extras: Extiende las funcionalidades de la librería leaflet proporcionando características adicionales para la creación de mapas interactivos más avanzados.

GGally: Proporciona extensiones para ggplot2, permitiendo crear gráficos de matriz y otras visualizaciones complejas de forma más sencilla.

Carga de datos para el análisis.

Leemos el dataset desde su ubicación y especificamos que el separador de campos es un punto y coma.

library(tidyverse)

# Leer el archivo CSV
airbnb_data_raw <- read.csv("../data/raw/air-bnb-listings.csv", sep = ';')

Mostramos las primeras filas del dataset, para comprobar que los datos se han cargado correctamente.

# Ver las primeras filas de los datos
head(airbnb_data_raw)

Calculamos y mostramos las dimensiones originales del dataset, obteniendo el número de filas y columnas.

dimension_original <- dim(airbnb_data_raw)

paste('El numero de filas que tiene el dataframe airbnb_data es:', dimension_original[1])
## [1] "El numero de filas que tiene el dataframe airbnb_data es: 21278"
paste('El numero de columnas que tiene el dataframe airbnb_data es:', dimension_original[2])
## [1] "El numero de columnas que tiene el dataframe airbnb_data es: 17"

El dataset original, tiene 17 variables(columnas) y 21278 observaciones(filas).

Examinamos que tipo de variables tenemos.

str(airbnb_data_raw)
## 'data.frame':    21278 obs. of  17 variables:
##  $ Room.ID                    : int  36707573 21859113 21862103 21875158 21932504 22042300 22043848 22048206 22066174 22079865 ...
##  $ Name                       : chr  "Soggiorno Madrid-Hospitality , comfort & passion." "Be Mate Plaza España Wild 203" "Designer flat in Madrid's exclusive Salamanca area" "Estudio de diseño en pleno centro de Madrid" ...
##  $ Host.ID                    : int  275911703 157114944 8851341 159570292 160055902 160981040 160967778 105797031 54343289 34563914 ...
##  $ Neighbourhood              : chr  "Centro Storico" "Argüelles" "Recoletos" "Sol" ...
##  $ Room.type                  : chr  "Private room" "Entire home/apt" "Entire home/apt" "Private room" ...
##  $ Room.Price                 : int  155 147 625 500 62 12 70 69 81 50 ...
##  $ Minimum.nights             : int  1 1 5 1 2 15 4 10 1 7 ...
##  $ Number.of.reviews          : int  1 1 2 110 194 11 109 51 87 12 ...
##  $ Date.last.review           : chr  "2019-12-19" "2018-12-01" "2018-08-09" "2020-02-26" ...
##  $ Number.of.reviews.per.month: num  0.16 0.05 0.06 3.41 6.16 0.4 3.43 1.64 2.74 0.44 ...
##  $ Rooms.rent.by.the.host     : int  3 36 1 2 1 3 6 2 6 1 ...
##  $ Availibility               : int  173 0 177 347 286 106 125 47 0 301 ...
##  $ Updated.Date               : chr  "2020-06-19" "2020-07-17" "2020-07-17" "2020-07-17" ...
##  $ City                       : chr  "Florence" "Madrid" "Madrid" "Madrid" ...
##  $ Country                    : chr  "Italy" "Spain" "Spain" "Spain" ...
##  $ Coordinates                : chr  "43.78225294444298, 11.241335882500344" "40.421911823829916, -3.716298875039167" "40.42205487717857, -3.6889613202974916" "40.41573794398538, -3.7044166450981693" ...
##  $ Location                   : chr  "Italy, Florence, Centro Storico" "Spain, Madrid, Argüelles" "Spain, Madrid, Recoletos" "Spain, Madrid, Sol" ...

Vemos que tenemos variables categóricas como Room.type, Neighbourhood, City y Country que no se están tomando como tal, sino como character. Lo mismo sucede con las fechas.

Para tener más visibilidad del tipo y clase de dato que tiene cada columna, creamos un DataFrame que muestre la informacion sobre la clase y el tipo de datos de cada variable.

# Función para obtener las clases y tipos de las columnas de un dataframe
obtener_clase_tipo <- function(data) {
  # Crear un vector con los nombres de las columnas
  column_names <- colnames(data)

  # Obtener la clase y el tipo de cada columna utilizando sapply
  column_classes <- sapply(data, class)
  column_types <- sapply(data, typeof)

  # Crear el dataframe con la información recopilada de Clase y Tipo
  clase_tipo <- data.frame(
    Class = column_classes,     # Clase de cada columna
    Type = column_types         # Tipo de cada columna
  )
  
  # Devolver el dataframe
  return(clase_tipo)
}

# Usar la función con el dataframe airbnb_data
resultado_clase_tipo <- obtener_clase_tipo(airbnb_data_raw)
print(resultado_clase_tipo)

Con esta información, vamos a proceder a hacer las conversiones necesarias:

Conversión de variables

Para asegurar la integridad del dataframe original mientras realizamos cambios, primero crearemos una copia del mismo. Todas las modificaciones y análisis se realizarán en esta copia, denominada airbnb_data.

airbnb_data <- data.frame(airbnb_data_raw)

Como hemos visto arriba, hay columnas que deberían ser de diferente clase.

str(airbnb_data[, c("Date.last.review", "Updated.Date", "Room.type", "Neighbourhood", "City", "Country")])
## 'data.frame':    21278 obs. of  6 variables:
##  $ Date.last.review: chr  "2019-12-19" "2018-12-01" "2018-08-09" "2020-02-26" ...
##  $ Updated.Date    : chr  "2020-06-19" "2020-07-17" "2020-07-17" "2020-07-17" ...
##  $ Room.type       : chr  "Private room" "Entire home/apt" "Entire home/apt" "Private room" ...
##  $ Neighbourhood   : chr  "Centro Storico" "Argüelles" "Recoletos" "Sol" ...
##  $ City            : chr  "Florence" "Madrid" "Madrid" "Madrid" ...
##  $ Country         : chr  "Italy" "Spain" "Spain" "Spain" ...

Data.last.review y Update.Date.

Deberían ser fecha: Ya que ambas variables permiten cálculos como la diferencia de días entre eventos, agrupación por períodos de tiempo (por ejemplo, mes, año), y correlacionar eventos o cambios con fechas específicas, lo cual sería difícil si estas variables fueran tratadas de otra manera (como character por ejemplo).

Room.type, Neighbourhood, City y Country

Deberían ser categóricas: Ya que agrupan los datos usando etiquetas textuales y facilitan análisis por segmentos. Además, no representan cantidades ni establecen un orden, características típicas de las variables categóricas.

Data.last.review y Update.Date

Convertimos ambas columnas a fechas:

# Convertir la columna Date.last.review a tipo fecha
airbnb_data$Date.last.review <- as.Date(airbnb_data$Date.last.review, format = "%Y-%m-%d")

# Convertir la columna Updated.Date a tipo fecha
airbnb_data$Updated.Date <- as.Date(airbnb_data$Updated.Date, format = "%Y-%m-%d")

Comprobamos que se ha realizado con éxito el cambio:

str(airbnb_data[, c("Date.last.review", "Updated.Date")])
## 'data.frame':    21278 obs. of  2 variables:
##  $ Date.last.review: Date, format: "2019-12-19" "2018-12-01" "2018-08-09" "2020-02-26" ...
##  $ Updated.Date    : Date, format: "2020-06-19" "2020-07-17" "2020-07-17" "2020-07-17" ...

Como podemos comprobar arriba, el cambio se hizo correctamente. Data.last.review y Update.Date son ahora fechas.

Room.type, Neighbourhood, City y Country

Convertimos estas variables en categóricas.

# Convertir columnas a factores (categóricas)
airbnb_data$Room.type <- as.factor(airbnb_data$Room.type)
airbnb_data$Neighbourhood <- as.factor(airbnb_data$Neighbourhood)
airbnb_data$City <- as.factor(airbnb_data$City)
airbnb_data$Country <- as.factor(airbnb_data$Country)

Nos aseguramos que los cambios se han hecho bien:

summary(airbnb_data[, c("Room.type", "Neighbourhood", "City", "Country")])
##            Room.type         Neighbourhood              City                Country     
##  Entire home/apt:12719   Embajadores: 2559   Madrid       :21255   Spain        :21269  
##  Hotel room     :  221   Universidad: 2059   Girona       :    6   Italy        :    2  
##  Private room   : 8006   Palacio    : 1490   Barcelona    :    5   United states:    2  
##  Shared room    :  332   Sol        : 1366   Mallorca     :    2   Australia    :    1  
##                          Justicia   : 1116   San-francisco:    2   Belgium      :    1  
##                          Cortes     :  976   Brussels     :    1   Portugal     :    1  
##                          (Other)    :11712   (Other)      :    7   (Other)      :    2

Con la información de arriba, comprobamos que efectivamente que Room.type, Neighbourhood, City y Country son ahora variables catégoricas.

City y Country

Podemos notar que en City y Country, hay algo que llama la atención. Se incluyen otras ciudades y países que no deberían estar.

summary(airbnb_data[, c("City", "Country")])
##             City                Country     
##  Madrid       :21255   Spain        :21269  
##  Girona       :    6   Italy        :    2  
##  Barcelona    :    5   United states:    2  
##  Mallorca     :    2   Australia    :    1  
##  San-francisco:    2   Belgium      :    1  
##  Brussels     :    1   Portugal     :    1  
##  (Other)      :    7   (Other)      :    2

Vemos que no todos los registros que pertenecen a España, son de Madrid.

Hacemos un filtrado en el que solo mantenemos las filas donde el valor de la columna City es Madrid.

# Filtrar el dataset para mantener solo las filas donde la ciudad es Madrid
airbnb_data <- airbnb_data |> filter(City == "Madrid")

Una vez hecho el filtro, verificamos que en City, no aparezca nada que no sea Madrid.

# Verificar que sólo quedan entradas de Madrid en el dataset original
summary(airbnb_data[, c("City", "Country")])
##         City            Country     
##  Madrid   :21255   Spain    :21255  
##  Barcelona:    0   Australia:    0  
##  Brussels :    0   Belgium  :    0  
##  Florence :    0   Italy    :    0  
##  Girona   :    0   Portugal :    0  
##  Istanbul :    0   Turkey   :    0  
##  (Other)  :    0   (Other)  :    0

La información de arriba, es lo que esperabamos, solo quedan los registros de Madrid.
Sin embargo siguen apareciendo otros niveles tanto en City y Country. Ya no los necesitamos, así los vamos a eliminar.

airbnb_data$City <- droplevels(airbnb_data$City)
airbnb_data$Country <- droplevels(airbnb_data$Country)

#Verificamos que solo queden los niveles deseados en City y Country
summary(airbnb_data[, c("City", "Country")])
##      City        Country     
##  Madrid:21255   Spain:21255

Availibility

Transformamos la columna Availibility en una variable categórica, definiendo rangos específicos que representan distintos niveles de disponibilidad.

Esta categorización nos permite analizar y visualizar la información de forma más eficiente.

# Rangos de disponibilidad y sus etiquetas
breaks <- c(-1, 0, 30, 60, 90, 180, 365)
labels <- c("No disponible", "Hasta 30 días", "31 a 60 días", "61 a 90 días", "91 a 180 días", "181 a 365 días")

# Categorizar 'Availability' usando cut()
airbnb_data$Availability_Cat <- cut(airbnb_data$Availibility, breaks = breaks, labels = labels, include.lowest = TRUE)


airbnb_data$Availability_Cat<- as.factor(airbnb_data$Availability_Cat)

Realizamos una revisión de la nueva columna para asegurarnos de que la asignación de categorías se hizo correctamente

summary(airbnb_data$Availability_Cat)
##  No disponible  Hasta 30 días   31 a 60 días   61 a 90 días  91 a 180 días 181 a 365 días 
##           5465           1519            815           1982           3288           8186

Coordinates

La columna Coordinates contiene pares de valores separados por comas que representan la latitud y longitud geográfica

head(airbnb_data$Coordinates)
## [1] "40.421911823829916, -3.716298875039167"  "40.42205487717857, -3.6889613202974916"  "40.41573794398538, -3.7044166450981693" 
## [4] "40.421747567976034, -3.710994512030235"  "40.391493766002014, -3.6450831761057922" "40.418435611771486, -3.7013169106966464"

Dividimos Coordinates en dos columnas, Latitude y Longitude que representan las coordenadas geográficas.

# Asegurándonos de que la columna Coordinates está en formato de string
airbnb_data$Coordinates <- as.character(airbnb_data$Coordinates)

# Separar la columna Coordinates en dos nuevas columnas: Latitude y Longitude
airbnb_data<- airbnb_data |>
  separate(Coordinates, into = c("Latitude", "Longitude"), sep = ",", convert = TRUE)

Nos aseguramos de que el cambio se ha hecho efectivo:

str(airbnb_data[, c("Latitude", "Longitude")])
## 'data.frame':    21255 obs. of  2 variables:
##  $ Latitude : num  40.4 40.4 40.4 40.4 40.4 ...
##  $ Longitude: num  -3.72 -3.69 -3.7 -3.71 -3.65 ...

Como podemos comprobar, ahora la columna Coordinates se ha convertido en 2: Latitude y Longitude

Location

Parece que la columna Location es la combinación de Country, City, y Neighbourhood. Vamos a probarlo:

#Crea una nueva columna temporal llamada Combined que combina Country,City y Neighbourhood, separados por ",". 

airbnb_data <- airbnb_data |> mutate(Combined = paste(Country, City, Neighbourhood, sep = ", "))

#Compara la columna Location con la columna Combined para verificar si son iguales.

airbnb_data <- airbnb_data |> mutate(Location_Match = Location == Combined)


#Revisa cuántos y qué porcentaje de las filas tienen coincidencia exacta entre Location y la columna combinada.

table(airbnb_data$Location_Match) #Cuenta solo lo que es TRUE
## 
##  TRUE 
## 21255

Comparamos la columna Location con la combinación de Country, City, y Neighbourhood.

Podemos ver que son iguales. Location es redundante por lo que se puede eliminar.

También elimaremos las columnas temporales que creamos para llegar a esta conclusiónLocation_Match y Combined:

#Eliminar Location, Location_Match y Combined
airbnb_data <- select(airbnb_data, -Location)
airbnb_data <- select(airbnb_data, -Location_Match)
airbnb_data <- select(airbnb_data, -Combined)

Estudio de los valores nulos y ceros.

Creamos un dataframe solo con las columnas que tienen nulos o ceros

# Dataframe con la contabilización de Nulos y Ceros
resultados <- data.frame(
  NA_Count = colSums(is.na(airbnb_data)),
  Zero_Count = colSums(airbnb_data == 0, na.rm = TRUE)
)

# Filtrar y mostrar solo las filas con NA o ceros
resultados_con_NA_o_ceros <- resultados[apply(resultados, 1, function(x) any(x > 0)), ]
print(resultados_con_NA_o_ceros)

Room.Price

paste("Total suma de filas de Room.Price con ceros:", sum(airbnb_data$Room.Price == 0))
## [1] "Total suma de filas de Room.Price con ceros: 1"

Eliminamos el registro con Room.Price de cero porque probablemente es un error de entrada, ya que es poco plausible que una habitación de hotel tenga un coste cero. Este error podría distorsionar nuestro análisis estadísticos y posterior modelo predictivo. Eliminando este dato, aseguramos la calidad y consistencia del dataset.

Number.of.reviews, Date.last.review y Number.of.reviews.per.month.

Vemos que existe la misma cantidad de ceros en Number.of.reviews, que nulos en Date.last.review y Number.of.reviews.per.month.

¿Están relacionados estos tres valores?

Vamos a verlo:

# Añadir una columna temporal para verificar la condición
airbnb_data <- airbnb_data |>
  mutate(
    check = Number.of.reviews == 0 & is.na(Date.last.review) & is.na(Number.of.reviews.per.month)
  )

# Sumar para comprobar la condición basada en Number.of.reviews
result <- airbnb_data |>
  filter(Number.of.reviews == 0) |>
  summarise(
    Total_Zero_Reviews = n(),
    Correct_Cases = sum(check)
  )

print(result)

Creamos una columna llamada check. Esta columna nos ayuda a ver si el número de reseñas = 0 y, al mismo tiempo, si las fechas de la última reseña y el promedio de reseñas por mes están vacíos. Si se cumplen estas condiciones, la columna indica True; si no, muestra False.

Comparamos, la columna check con la cuenta de los registros donde el número de reseñas es cero.

Este paso nos permite comprobar si nuestra teoría sobre la relación entre estas variables se sostiene.

Y si, tiene sentido, ya que si un alojamiento no ha recibido reseñas, no podría tener una fecha de última reseña ni un promedio de reseñas mensuales.

Ahora elimaremos la columna temporal check.

#Eliminar check
airbnb_data <- select(airbnb_data, -check)
Categorización de Number.of.reviews y Number.of.reviews.per.month

Vamos a categorizar Number.of.reviews y Number.of.reviews.per.monthpara un mejor análisis

Empezamos colocando ceros en los valores nulos de Number.of.reviews.per.month, paraasegurarmos podamos operar sobre esta columna, más adelante.

sum(is.na(airbnb_data$Number.of.reviews.per.month))
## [1] 5400

Tenemos 5400 registros vacíos de Number.of.reviews.per.month.

Sustituimos NA por el valor cero.

# Sustituir directamente los NA por 0 en la columna específica
airbnb_data$Number.of.reviews.per.month[is.na(airbnb_data$Number.of.reviews.per.month)] <- 0
#Calculo el total de filas con NA 
paste("Total suma de filas NA:", sum(is.na(airbnb_data$Number.of.reviews.per.month)))
## [1] "Total suma de filas NA: 0"
#Calculo el total de filas con ceros 
paste("Total suma de filas con ceros:", sum(airbnb_data$Number.of.reviews.per.month == 0))
## [1] "Total suma de filas con ceros: 5400"

Como podemos comprobar, el cambio se ha hecho correctamente. Los 5400 registros vacíos de Number.of.reviews.per.month ahora contienen ceros.


Ahora categorizamos la variable:

airbnb_data <- airbnb_data |> 
  mutate(
    Review_category = factor(
      cut(
        Number.of.reviews.per.month,
        breaks = c(-Inf, 0, 1, 5, Inf),  # Incluyendo  la categoría de "sin reseñas" en los breaks
        labels = c('sin reseñas', '0-1/mes', '1-5/mes', '+5/mes'),
        include.lowest = TRUE,
        right = TRUE
      )
    )
  )


summary(airbnb_data$Review_category)
## sin reseñas     0-1/mes     1-5/mes      +5/mes 
##        5400        8771        6452         632
#NOTA: 
#El intervalo sin reseñas, incluye todos los nulos
#El intervalo 0-1/mes, incluye los valores desde 0.01 reviews.per.month a 1 (incluido)
#El intervalo 1-5/mes, incluye los valores desde 1,01 reviews.per.month a 5 (incluido) y asi sucesivamente 

Si el valor es cero, lo categorizamos como “sin reseñas”. Luego, los valores se dividen en los siguientes rangos: 0-1/mes, 1-5/mes, 5-10/mes y +10/mes. Finalmente, creamos una nueva variable llamada Review_category que contiene estas categorías.

Ahora categorizamos la variable Number.of.reviews:

airbnb_data <- airbnb_data |> 
  mutate(
    Review_Count_Category = factor(
      cut(
        Number.of.reviews,
        breaks = c(-Inf, 0, 1, 10, 50, 100, Inf),  # Incluyendo la condición de 0 reseñas directamente en los breaks
        labels = c('sin reseñas', '1 reseña', '2-10 reseñas', '11-50 reseñas', '51-100 reseñas', '+ 101 reseñas'),
        include.lowest = TRUE,
        right = TRUE
      ),
      levels = c('sin reseñas', '1 reseña', '2-10 reseñas', '11-50 reseñas', '51-100 reseñas', '+ 101 reseñas')
    )
  )

# Puedes verificar el resultado con:
summary(airbnb_data$Review_Count_Category)
##    sin reseñas       1 reseña   2-10 reseñas  11-50 reseñas 51-100 reseñas  + 101 reseñas 
##           5400           1791           4871           4749           2078           2366
#NOTA: 
#El intervalo sin reseñas, incluye todos los ceros
#El intervalo1 reseña, incluye solos los valor 1 
#El intervalo '2-10 reseñas', incluye los valores desde 2 a 10 (incluido)
#El intervalo '11-50 reseñas', incluye los valores desde 11 a 50 (incluido) y asi sucesivamente 

Si hay cero reseñas, se etiqueta como “sin reseñas”. De lo contrario, se divide en varios rangos, como 1 reseña, 2-10 reseñas, 11-50 reseñas, etc. Luego, se crea una nueva variable llamada Review_Count_Category con estas categorías.

Date.last.review

Es fundamental considerar la actualidad de la información y su influencia en los análisis y decisiones basadas en estos datos. Vamos a calcular la cantidad de días transcurridos desde el último scraping hasta la fecha de extracción de los datos.

# Calcular el número de días desde la última revisión hasta la fecha de extracción
#Asumimos que la fecha del ultimo scrapping fue

date_scraped <- as.Date("2020-08-07")

airbnb_data$time_since_last_review <- as.numeric(difftime(date_scraped, airbnb_data$Date.last.review, units = "days"))

Se calcula el tiempo transcurrido en días desde la fecha de extracción de datos, representada por “2020-08-07”, hasta la fecha de la última reseña. La diferencia se almacena en una nueva columna llamada time_since_last_review

Ahora veamos la distribución de los datos en time_since_last_review

# Histograma del tiempo transcurrido desde la última reseña
ggplot(airbnb_data, aes(x = time_since_last_review)) +
  geom_histogram(bins = 30, fill = "#69b3a2", color = "#e9ecef") + # Colores personalizados para las barras
  theme_minimal() +
  theme(
    text = element_text(family = "Helvetica", size = 12), # Cambia la fuente y el tamaño del texto
    plot.title = element_text(face = "bold", hjust = 0.5), # Negrita y centrado para el título
    plot.subtitle = element_text(face = "italic", hjust = 0.5), # Subtítulo en cursiva y centrado
    axis.title = element_text(face = "bold", size = 13) # Títulos de los ejes en negrita y tamaño aumentado
  ) +
  labs(
    title = "Distribución de días desde la última reseña",
    subtitle = "Visualización de la frecuencia de días transcurridos",
    x = "Días desde la última reseña", y = "Frecuencia"
  )
## Warning: Removed 5400 rows containing non-finite outside the scale range (`stat_bin()`).

Para poder realizar un mejor análisis de los datos, vamos a categorizar las nueva columna: time_since_last_review

airbnb_data <- airbnb_data |>
  mutate(
    Time_category = factor(
      if_else(
        is.na(time_since_last_review),
        'sin reseñas',  # Asignar directamente 'sin reseñas' si el valor es NA
        cut(  # Usar cut solo en valores no-NA
          time_since_last_review,
          breaks = c(0, 60, 182, 365, Inf),  # Definiendo los rangos
          labels = c('hasta 8 semanas', '8 semanas - 6 meses', '6-12 meses', '1+ año'),
          include.lowest = TRUE,
          right = TRUE
        )
      ),
      levels = c('sin reseñas', 'hasta 8 semanas', '8 semanas - 6 meses', '6-12 meses', '1+ año')
    )
  )

summary(airbnb_data$Time_category)
##         sin reseñas     hasta 8 semanas 8 semanas - 6 meses          6-12 meses              1+ año 
##                5400                1672                7150                3802                3231
#NOTA: 
#El intervalo sin reseñas, incluye todos los nulos
#El intervalo hasta 8 semanas, incluye los valores hasta e 60 (incluida)
#El intervalo 8 semanas - 6 meses, incluye los valores desde 61 a 182 (incluido)  y asi sucesivamente 

Availibility

#Calculo el total de filas con ceros 
paste("Total suma de filas con ceros en la columna Availibility es:", sum(airbnb_data$Availibility == 0))
## [1] "Total suma de filas con ceros en la columna Availibility es: 5465"

Los ceros de Availibility, se han tratado en la columna Availibility_Cat como alojamientos “No disponibles”

paste("Total suma de filas con 'No disponible' en la columna Availability_Cat es:", sum(airbnb_data$Availability_Cat == "No disponible"))
## [1] "Total suma de filas con 'No disponible' en la columna Availability_Cat es: 5465"

Vemos que el recuento de “No disponible” en Availibility_Cat, coincide con el recuento de ceros en Availibility.

Comprobación de Duplicados

Veamos sin en nuestro dataset, hay filas duplicadas.

filas_duplicadas <- airbnb_data[duplicated(airbnb_data), ]
if (nrow(filas_duplicadas) == 0) {
  print("No hay filas duplicadas en el dataset.")
} else {
  print("Se han encontrado filas duplicadas en el dataset.")
}
## [1] "No hay filas duplicadas en el dataset."

Utilizamos duplicated() para crear un vector que identifica filas repetidas en airbnb_data, almacenando dichas filas en filas_duplicadas. Luego, contamos cuantas filas hay en filas_duplicadas, con nrow().

Dependiendo del resultado, imprime un mensaje indicando la presencia o ausencia de filas duplicadas. En este caso, podemos afirmar que no hay filas duplicadas.

Resumen cambios en las variables.

Resumen condensado y ajustado de las transformaciones aplicadas:

1. Date.last.review y Updated.Date

  • Convertidas a formato de fecha (%Y-%m-%d).
  • Calculada la diferencia en días desde la fecha del último scraping (2020-08-07) hasta Date.last.review. El resultado se almacena en Time_category y se categoriza en:
    • “sin reseñas” (casos donde la fecha es nula)
    • “hasta 8 semanas”
    • “8 semanas - 6 meses”
    • “6-12 meses”
    • “1+ año”

2. Room.type, Neighbourhood, City, Country

  • Transformadas en variables categóricas (factores) para facilitar análisis por segmentos.

3. Availability

  • Categorizada en la columna Availibility_Catdesde ‘No disponible’ hasta ‘181 a 365 días’ en rangos que representan distintos niveles de disponibilidad:
    • “No disponible” (para alojamientos con cero días disponibles)
    • “Menos de 30 días”
    • “31 a 60 días”
    • “61 a 90 días”
    • “91 a 180 días”
    • “181 a 365 días”

4. City

  • Filtrados los datos para incluir solo entradas de Madrid.

5. Coordinates

  • Separada en dos columnas numéricas: Latitude y Longitude.

6. Location

  • Eliminada tras confirmar redundancia con Country, City, y Neighbourhood.

7. Number.of.reviews & Number.of.reviews.per.month

  • Análisis de valores nulos y ceros; categorizados para facilitar análisis:
    • Review_Count_Category (basado en Number.of.reviews):

      • “sin reseñas”
      • “1 reseña”
      • “2-10 reseñas”
      • “11-50 reseñas”
      • “51-100 reseñas”
      • “101-500 reseñas”
      • “500+ reseñas”
    • Review_category (basado en Number.of.reviews.per.month):

      • “sin reseñas” (incluye registros nulos y ceros)
      • “0-1/mes”
      • “1-5/mes”
      • “5-10/mes”
      • “10-20/mes”
      • “20+ mes”

También se ha comprobado que no existan duplicados en el dataset.

Gráficos

Gráficos por variables

Variables Categóricas

1. Neighbourhood

#Histogramas

# Grafico para barrio  

# Calcular la frecuencia de cada barrio
neighbourhood_freq <- airbnb_data |>
  count(Neighbourhood) |>
  arrange(desc(n)) |>
  top_n(10, n)  # Selecciona solo los top 10

# Graficar la distribución por barrio solo para los top 10
ggplot(neighbourhood_freq, aes(x = n, y = reorder(Neighbourhood, n))) +
  geom_bar(stat = "identity", fill = "steelblue") +  # Cambia el color de las barras
  theme_minimal() +
  theme(axis.text.y = element_text(size = 15),  # Aumenta el tamaño del texto del eje y
        plot.title = element_text(size = 22, face = "bold")) +  # Aumenta y pone en negrita el título
  labs(title = "Distribución por Barrio", x = "Frecuencia", y = "Barrio")

Observaciones clave del gráfico:

El gráfico representa la frecuencia de listados de Airbnb en los top 10 barrios más populares. Cada barra representa un barrio diferente, ordenados de mayor a menor frecuencia de arriba hacia abajo.

  1. Barrios (Eje Y): Desde la parte superior, los barrios listados son Embajadores, Universidad, Palacio, Sol, Justicia, Cortes, Trafalgar, Palos de Moguer, Goya, y Puerta del Angel.

  2. Frecuencia (Eje X): La escala del eje X va desde 0 hasta más de 2000, indicando el número de listados en cada barrio.

El barrio de Embajadores tiene la mayor cantidad de listados, seguido por Universidad y Palacio. Los barrios de Goya y Puerta del Angel, al final de la lista, tienen considerablemente menos listados comparados con los más altos en la gráfica.

Suposiciones

  • Suposición 1: Los barrios con mayor número de listados, como Embajadores y Universidad, probablemente demanden precios más altos debido a su popularidad y atractivos locales.

  • Suposición 2: Los barrios con menos listados pueden ofrecer oportunidades para establecer precios competitivos, atrayendo a los huéspedes que buscan opciones más económicas o menos concurridas.

2. Room.type

# Gráfico para Room.type
ggplot(airbnb_data, aes(x = Room.type)) +
  geom_bar(fill = "coral") +
  labs(title = "Distribución por Tipo de Habitación", x = "Tipo de Habitación", y = "Frecuencia")


Las barras representan la frecuencia de cada tipo de habitación en el dataset.

  • Tipos de Habitación (Eje X): Las categorías mostradas son “Entire home/apt” (casa o apartamento completo), “Hotel room” (habitación de hotel), “Private room” (habitación privada) y “Shared room” (habitación compartida).

  • Frecuencia (Eje Y): El eje vertical muestra la cantidad de listados, con una escala que va de 0 a más de 10,000.

Observaciones clave del gráfico:

  • Casa o apartamento completo tiene la frecuencia más alta, indicando que es el tipo de alojamiento más comúnmente listado.

  • Habitación privada es el segundo tipo más común, aunque con una frecuencia significativamente menor en comparación con las casas o apartamentos completos.

  • Habitación de hotel y habitación compartida tienen las menores frecuencias, con la habitación compartida mostrando la menor popularidad entre los listados.

Suposiciones

  • Suposición 1: Los alojamientos tipo “Entire home/apt” (casa o apartamento completo) pueden comandar precios más altos debido a su mayor demanda y la privacidad que ofrecen.

  • Suposición 2: Las “Private room” (habitación privada) y “Shared room” (habitación compartida) podrían necesitar precios más ajustados para competir, especialmente en áreas con alta disponibilidad de casas o apartamentos completos.

3. Availability_Cat

# Gráfico para Availability_Cat
ggplot(airbnb_data, aes(x = Availability_Cat)) +
  geom_bar(fill = "purple") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(title = "Distribución por Categoría de Disponibilidad", x = "Categoría de Disponibilidad", y = "Frecuencia")

Las barras representan la cantidad de propiedades disponibles según diferentes rangos de días al año en que están disponibles para ser alquiladas.

  • Categorías de Disponibilidad (Eje X): Se dividen en seis categorías distintas: “No disponible”, “Hasta 30 días”, “31 a 60 días”, “61 a 90 días”, “91 a 180 días”, y “181 a 365 días”.

  • Frecuencia (Eje Y): El eje vertical indica la cantidad de listados, con una escala que va desde 0 hasta más de 8000.

Observaciones clave del gráfico:

  • No disponible y 181 a 365 días son las categorías con la mayor frecuencia, indicando que una gran cantidad de propiedades no están disponibles para alquiler o están disponibles casi todo el año.

  • Las demás categorías presentan una menor frecuencia, con la categoría “31 a 60 días” mostrando la menor cantidad de propiedades. Esto sugiere que menos propiedades están disponibles por períodos intermedios de tiempo.

  • Las propiedades que se alquilan por “91 a 180 días” tienen una frecuencia considerablemente más alta que las de corto plazo pero menor que las de muy largo plazo, reflejando posiblemente una preferencia por alquileres de medio término.

Suposiciones:

  • Suposición 1: Las propiedades listadas como disponibles “181 a 365 días” pueden establecer precios más altos durante temporadas de alta demanda, aprovechando su constante disponibilidad.

  • Suposición 2: Propiedades con menor disponibilidad (“Hasta 30 días”) podrían necesitar precios dinámicos, ajustándose rápidamente a las demandas del mercado para maximizar los ingresos en periodos cortos.

4. Time_category

# Gráfico para Time_category
ggplot(airbnb_data, aes(x = Time_category)) +
  geom_bar(fill = "yellow") +
  labs(title = "Distribución de Time_category (Resta entre Scrapping date y Date.last.review)", x = "Time_category", y = "Frecuencia")

Observaciones clave del gráfico:

El gráfico clasifica las propiedades de Airbnb según el tiempo transcurrido desde la última reseña, distribuyendo esta información a lo largo de cinco categorías distintas. El eje X representa las categorías de tiempo, y el eje Y muestra la frecuencia de listados en cada categoría.

  • Sin reseñas: Mayor frecuencia, indicando muchas propiedades sin reseñas recientes.

  • Hasta 8 semanas: Frecuencia significativamente más baja, menos propiedades revisadas recientemente.

  • 8 semanas - 6 meses: Mayor frecuencia, refleja un volumen considerable de propiedades con reseñas moderadamente recientes.

  • 6-12 meses: Frecuencia más baja que la categoría “8 semanas - 6 meses”, muestra menos actividad reciente.

  • 1+ año: Frecuencia similar a “sin reseñas”, muchas propiedades sin reseñas en el último año.

Suposiciones:

  1. Suposición 1: Las propiedades en la categoría “1+ año” sin reseñas recientes podrían ser percibidas como menos deseables, necesitando estrategias de precios más atractivas o promociones para aumentar la ocupación.

  2. Suposición 2: Propiedades con reseñas recientes (“Hasta 8 semanas”) pueden aprovechar su reciente popularidad para establecer precios ligeramente más altos, atrayendo huéspedes que valoran la frescura y la actividad positiva reciente.

5. Review_category

# Gráfico para Review_category
ggplot(airbnb_data, aes(x = Review_category)) +
  geom_bar(fill = "green") +
  labs(title = "Distribución de Review_category", subtitle ="(Categorizacion de Number.of.reviews.per.month)", x = "Review_category", y = "Frecuencia")

Observaciones clave del gráfico:

El gráfico “Distribución de Review_category” muestra la frecuencia de propiedades de Airbnb según el número de reseñas que reciben por mes. Las categorías van desde “sin reseñas” hasta “más de 5 reseñas por mes”. El eje X categoriza el número de reseñas mensuales y el eje Y muestra la frecuencia de propiedades en cada categoría.

  • Sin Reseñas: Representa una frecuencia moderada, lo que indica que un número considerable de propiedades no ha recibido reseñas.

  • 0-1/mes: Esta categoría tiene la frecuencia más alta, sugiriendo que la mayoría de las propiedades reciben entre cero y una reseña por mes.

  • 1-5/mes: La segunda categoría más frecuente, mostrando una actividad moderada en términos de reseñas.

  • +5/mes: Tiene menos frecuencia en comparación con las categorías de 0-1 y 1-5 reseñas por mes, lo que indica que pocas propiedades reciben más de cinco reseñas mensuales.

Suposiciones Basadas en el Gráfico:

  • Suposición 1: Propiedades en la categoría “+5/mes” demuestran alta demanda y satisfacción del cliente, lo que podría justificar precios más elevados debido a la percepción de calidad y experiencia superior.

  • Suposición 2: Las propiedades en la categoría “0-1/mes” podrían necesitar ajustar sus precios o mejorar su oferta para incrementar su atractivo y frecuencia de reservas.

6. Review_Count_Category

# Gráfico para Review_Count_Category
ggplot(airbnb_data, aes(x = Review_Count_Category)) +
  geom_bar(fill = "blue") +
  labs(title = "Distribución de Review_Count_Category", subtitle ="(Categorizacion de Number.of.reviews)", x = "Review_Count_Category", y = "Frecuencia")

Observaciones clave del gráfico:

El gráfico muestra la distribución de propiedades de Airbnb según el número total de reseñas recibidas, categorizando desde “sin reseñas” hasta “más de 101 reseñas”. En el eje X se categoriza el número total de reseñas y el eje Y muestra la frecuencia de propiedades por categoría.

  • Sin reseñas: Mayor frecuencia, muchas propiedades sin reseñas.

  • 1 Reseña: Frecuencia menor que “sin reseñas”, notable descenso.

  • 2-10 Reseñas: Mayor que “1 Reseña”, indica varias propiedades moderadamente revisadas.

  • 11-50 Reseñas: Frecuencia similar a “2-10 Reseñas”, refleja estabilidad.

  • 51-100 Reseñas: Menor que las categorías intermedias, aún significativa.

  • +101 Reseñas: La menor frecuencia, pocas propiedades muy revisadas.

Suposiciones:

  1. Suposición 1: Las propiedades con pocas o ninguna reseña pueden necesitar estrategias de precios iniciales más atractivas y campañas promocionales para atraer a los primeros huéspedes y generar esas importantes primeras reseñas, ayudando a establecer credibilidad en la plataforma.

    Suposición 2: Las propiedades con un alto número de reseñas (51-100 y más de 101 reseñas) pueden aprovechar su estatus comprobado de popularidad y satisfacción del cliente para justificar precios más altos y atraer a huéspedes dispuestos a pagar más por una experiencia de calidad asegurada.

Variables Numéricas

1. Room.Price

# Boxplot para Room.Price
ggplot(airbnb_data, aes(y = Room.Price)) +
  geom_boxplot(fill = "lightblue", color = "darkblue") +
  labs(title = "Distribución del Precio de la Habitación",
       y = "Precio (€)",
       x = "") +
  theme_minimal()

2. Minimum.nights

# Boxplot para Minimum.nights
ggplot(airbnb_data, aes(y = Minimum.nights)) +
  geom_boxplot(fill = "lightgreen", color = "darkgreen") +
  labs(title = "Distribución del Número Mínimo de Noches Requeridas",
       y = "Número Mínimo de Noches",
       x = "") +
  theme_minimal()

3. Rooms.rent.by.the.host

# Boxplot para Rooms.rent.by.the.host
ggplot(airbnb_data, aes(y = Rooms.rent.by.the.host)) +
  geom_boxplot(fill = "lightyellow", color = "goldenrod") +
  labs(title = "Distribución del Número de Habitaciones alquiladas por el host",
       y = "Habitaciones alquiladas por el host",
       x = "") +
  theme_minimal()

Cuestiones sobre los datos.

Pregunta1: ¿Cuáles son los distritos más populares en Airbnb?

Histograma del vecindario en términos de distritos
# Calcular la frecuencia de cada distrito
distrito_frecuencia <- airbnb_data %>%
  count(Neighbourhood) %>%
  arrange(desc(n))  # Ordenar en orden descendente por frecuencia

# Obtener los top 10 distritos más frecuentes
top10_distritos <- distrito_frecuencia %>%
  top_n(10, n)

# Reorganizar el factor Neighbourhood en función de su frecuencia
airbnb_data$Neighbourhood <- factor(airbnb_data$Neighbourhood, levels = distrito_frecuencia$Neighbourhood)

# Filtrar solo las filas que corresponden a los 10 principales distritos
airbnb_data_top10 <- airbnb_data %>%
  filter(Neighbourhood %in% top10_distritos$Neighbourhood)

# Trazar el histograma
ggplot(airbnb_data_top10, aes(x = Neighbourhood)) +
  geom_bar(fill = "skyblue", color = "black") +
  labs(title = "Histograma Top10 del Vecindario en Términos de Distritos", subtitle="(Top 10, Orden Descendente)", 
       x = "Distrito", 
       y = "Frecuencia") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

El gráfico muestra un histograma del vecindario en términos de distritos, centrándose en los diez distritos más frecuentes en los datos de Airbnb. Los distritos están ordenados en orden descendente según su frecuencia. Cada barra representa la frecuencia de un distrito específico, lo que proporciona una visión general de la distribución de la popularidad de los distritos en el conjunto de datos.

Observaciones del Gráfico:

  • Embajadores es el distrito con la mayor frecuencia de listados, seguido de Universidad y Palacio, indicando que estas áreas son probablemente las más demandadas o con más oferta de alojamientos en Airbnb.

  • A partir de Sol en adelante, la frecuencia de los listados comienza a disminuir progresivamente.

  • Los distritos como Goya y Puerta del Angel muestran las menores frecuencias dentro de este top 10, lo que podría sugerir una menor popularidad o una oferta más limitada de alojamientos en estas áreas comparado con distritos como Embajadores o Universidad.

Pregunta2: ¿Existe una correlación entre los precios de las habitaciones y el número de reseñas que reciben, indicando la popularidad o la percepción de valor por parte de los usuarios?

Distribución de precio vs número de reseñas
ggplot(airbnb_data, aes(x = Room.Price, y = Number.of.reviews)) +

geom_point(color = "blue") +

labs(title = "Distribución de Precio vs Número de Reseñas", x = "Precio de la Habitación (€)", y = "Número de Reseñas")

Observaciones del Gráfico:

  1. Predominio de precios bajos y reseñas moderadas: La mayoría de los listados tienen precios inferiores a 1,000 euros y menos de 200 reseñas, indicando que las propiedades más asequibles son comunes pero no acumulan muchas reseñas.

  2. Propiedades caras con reseñas dispersas: Algunas propiedades con precios superiores a 2,500 euros tienen desde cero hasta muchas reseñas, lo que sugiere que un precio más alto no garantiza una mayor popularidad.

  3. Picos de popularidad a precios accesibles: Un número significativo de reseñas se observa en propiedades con precios razonables, lo que indica que ciertas propiedades a precios moderados son muy populares.

En resumen, no existe una correlación clara entre precios altos y un mayor número de reseñas, implicando que otros factores como ubicación y calidad podrían influir más en la popularidad.

Pregunta3: ¿Cómo varían los precios promedios de las diferentes tipos de habitaciones en Airbnb, y qué tipo de habitación tiene el precio promedio más alto en Madrid?


Precio promedio vs tipo de habitación

ggplot(airbnb_data, aes(x = Room.type, y = Room.Price)) +
  geom_bar(stat = "summary", fun = "mean", fill = "lightgreen", color = "darkgreen") +
  geom_hline(yintercept = mean(airbnb_data$Room.Price), color = "red", linetype = "dashed", linewidth = 1) +
  labs(title = "Precio Promedio vs Tipo de Habitación", x = "Tipo de Habitación", y = "Precio Promedio (€)")


El gráfico muestra el precio promedio de las habitaciones de Airbnb en relación con el tipo de habitación. Cada barra representa el precio promedio de un tipo específico de habitación, mientras que la línea roja punteada indica el precio promedio general de todas las habitaciones en el conjunto de datos. Esto proporciona una comparación visual entre los precios promedio de diferentes tipos de habitaciones, así como una referencia rápida del precio promedio general.

Observaciones del Gráfico:

  • Casa o Apartamento Completo: Este tipo de alojamiento tiene el precio promedio más alto, considerablemente más alto que los otros tipos, lo que indica que alquilar una propiedad completa es generalmente más costoso.

  • Habitación de Hotel: Tiene un precio promedio significativamente más bajo que un apartamento o casa completa, pero más alto que una habitación privada o compartida.

  • Habitación Privada: Presenta un precio promedio más bajo que la habitación de hotel.

  • Habitación Compartida: Es la opción más económica, con el precio promedio más bajo entre los tipos de habitaciones mostrados.

Pregunta4: ¿Cómo se distribuyen los diferentes tipos de habitaciones en los distintos distritos de Madrid y qué distritos presentan una mayor diversidad de opciones de alojamiento?

Tipo de propiedad y áreas (distritos) 

# Crear un gráfico de barras apiladas 

ggplot(data = airbnb_data, aes(x = Neighbourhood, fill = `Room.type`)) + geom_bar() + theme(axis.text.x = element_text(angle = 90, hjust = 1)) + labs(x = "Distrito", y = "Cantidad") + ggtitle("Distribución de tipos de propiedad por distritos en Madrid")

El gráfico muestra la distribución de los diferentes tipos de propiedades en cada distrito de Madrid. Cada barra representa un distrito y los segmentos de cada barra están coloreados para representar los diferentes tipos de propiedad. Los segmentos de cada barra se apilan unos sobre otros.

Pregunta5: ¿Cómo varían los precios de los alojamientos de Airbnb entre diferentes distritos de Madrid y cuáles presentan la mayor variabilidad en sus precios?

Distribución de precios vs distrito 

ggplot(data = airbnb_data, aes(x = Neighbourhood, y = Room.Price)) + geom_boxplot() + theme(axis.text.x = element_text(angle = 90, hjust = 1)) + labs(x = "Distrito", y = "Precio") + ggtitle("Distribución de precios por distrito en Madrid")


Este gráfico representa la distribución de los precios para cada distrito. Cada caja muestra la distribución de los precios en un distrito específico, donde la línea en el centro de la caja representa la mediana de los precios. Los puntos individuales fuera de los bigotes representan valores atípicos potenciales.

(Pending) Tendencia mediana y puntuación de reseñas 

ggplot(data = airbnb_data, aes(x = Room.Price, y = Number.of.reviews)) + geom_point() + geom_smooth(method = "lm", se = FALSE, color = "red") + labs(x = "Precio", y = "Puntuación  de reseñas") + ggtitle("Tendencia de la mediana de precios por puntuación de reseñas por distrito en Madrid")
## `geom_smooth()` using formula = 'y ~ x'


Este gráfico muestra la relación entre el precio y el puntuación de las reseñas recibidas. Cada punto en el gráfico representa un listado individual, donde la posición horizontal del punto indica el precio y la posición vertical indica el número de reseñas.

El gráfico incluye una línea de tendencia (en rojo) ajustada mediante un modelo de regresión lineal. Esta línea de tendencia representa la tendencia general entre el precio y el número de reseñas: si la línea de tendencia tiene una pendiente positiva, indica una relación positiva entre el precio y el número de reseñas, mientras que una pendiente negativa indica una relación negativa.

Gráfico de Distribución en Madrid

Pregunta: ¿Cómo se distribuyen geográficamente los listados de Airbnb en Madrid y qué áreas muestran mayor concentración de alojamientos?

Las columnas Latitude y Longitude están disponibles, podemos crear un mapa de puntos en Madrid:

# Crear un mapa con Leaflet
madrid_map <- leaflet(data = airbnb_data) |>
  addTiles()  # Añadir el mapa base

# Añadir la capa de mapa de calor
madrid_map <- madrid_map |>
  addHeatmap(
    lng = ~Longitude, lat = ~Latitude, intensity = ~1,
    blur = 20, max = 0.05, radius = 15
  )

# Establecer la vista inicial del mapa y mostrarlo
madrid_map <- madrid_map |>
  setView(lng = -3.703, lat = 40.416, zoom = 11)

# Imprimir el mapa
madrid_map
# Definir una función para determinar el color basado en el precio
get_color <- function(price) {
  if (price < 50) {
    "#00FF00"  # Verde para precios bajos
  } else if (price < 100) {
    "#FFFF00"  # Amarillo para precios medios
  } else {
    "#FF0000"  # Rojo para precios altos
  }
}

# Crear el mapa usando leaflet, aplicando directamente la función de colores
madrid_map <- leaflet(data = airbnb_data) %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  addCircleMarkers(
    ~Longitude, ~Latitude,
    radius = 5,
    color = ~sapply(Room.Price, get_color),
    fillOpacity = 0.5,
    stroke = FALSE,  # No dibujar bordes alrededor de los círculos
    group = "Airbnb Locations"
  ) %>%
  setView(lng = -3.703, lat = 40.416, zoom = 11)

# Imprimir el mapa
madrid_map

Pregunta: ¿Qué variables están más fuertemente correlacionadas con el precio de los alojamientos en Airbnb, como la cantidad de noches mínimas, el número de reseñas, o la cantidad de habitaciones que renta el anfitrión?

Mapa de Calor de Correlación

Para el mapa de calor de correlación, seleccionamos las columnas numéricas relevantes.

# Seleccionando columnas relevantes para la correlación
cor_data <- airbnb_data |>
  select(Room.Price, Minimum.nights, Number.of.reviews, Number.of.reviews.per.month, Rooms.rent.by.the.host) |>
  na.omit()  # Eliminando filas con valores NA para evitar errores

# Calculando la matriz de correlación
cor_matrix <- cor(cor_data)

# Generando el mapa de calor
corrplot(cor_matrix, method = "color", type = "upper", order = "hclust",
         tl.col = "black", tl.srt = 45, addrect = 2, 
         title = "Mapa de Calor de Correlación para AirBnB en Madrid",
         mar = c(0, 0, 2, 0))  # Ajusta el margen superior para dar más espacio al título

# Las columnas relevantes para la correlación, están guardadas en la variable cor_data
 
ggpairs(cor_data, 
        lower = list(continuous = wrap("points", alpha = 0.3,size=0.3  ,color='blue')), 
        upper = list(continuous = wrap("cor", size=4))
               )
## plot: [1, 1] [=====>------------------------------------------------------------------------------------------------------------------------------------] 4% est: 0s
## plot: [1, 2] [==========>-------------------------------------------------------------------------------------------------------------------------------] 8% est: 1s
## plot: [1, 3] [================>-------------------------------------------------------------------------------------------------------------------------] 12% est: 1s
## plot: [1, 4] [=====================>--------------------------------------------------------------------------------------------------------------------] 16% est: 1s
## plot: [1, 5] [===========================>--------------------------------------------------------------------------------------------------------------] 20% est: 1s
## plot: [2, 1] [================================>---------------------------------------------------------------------------------------------------------] 24% est: 1s
## plot: [2, 2] [======================================>---------------------------------------------------------------------------------------------------] 28% est: 1s
## plot: [2, 3] [===========================================>----------------------------------------------------------------------------------------------] 32% est: 1s
## plot: [2, 4] [=================================================>----------------------------------------------------------------------------------------] 36% est: 1s
## plot: [2, 5] [======================================================>-----------------------------------------------------------------------------------] 40% est: 1s
## plot: [3, 1] [============================================================>-----------------------------------------------------------------------------] 44% est: 1s
## plot: [3, 2] [=================================================================>------------------------------------------------------------------------] 48% est: 1s
## plot: [3, 3] [=======================================================================>------------------------------------------------------------------] 52% est: 1s
## plot: [3, 4] [============================================================================>-------------------------------------------------------------] 56% est: 1s
## plot: [3, 5] [==================================================================================>-------------------------------------------------------] 60% est: 1s
## plot: [4, 1] [=======================================================================================>--------------------------------------------------] 64% est: 1s
## plot: [4, 2] [=============================================================================================>--------------------------------------------] 68% est: 1s
## plot: [4, 3] [==================================================================================================>---------------------------------------] 72% est: 1s
## plot: [4, 4] [========================================================================================================>---------------------------------] 76% est: 0s
## plot: [4, 5] [=============================================================================================================>----------------------------] 80% est: 0s
## plot: [5, 1] [===================================================================================================================>----------------------] 84% est: 0s
## plot: [5, 2] [========================================================================================================================>-----------------] 88% est: 0s
## plot: [5, 3] [==============================================================================================================================>-----------] 92% est: 0s
## plot: [5, 4] [===================================================================================================================================>------] 96% est: 0s
## plot: [5, 5] [==========================================================================================================================================]100% est: 0s

Exportación dataset procesado

Ahora vamos a generar el dataset procesado:

# Exportar el dataframe a CSV con write.csv perteneciente a  'utils' que es un paquete base de R y siempre está cargado.
write.csv(airbnb_data, "../data/processed/dataset_airbnb_procesado.csv", row.names = FALSE)